package com.haozi.ehub.core.util;

import cn.hutool.core.util.CharsetUtil;
import cn.hutool.core.util.HexUtil;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.TypeReference;
import com.haozi.ehub.core.enums.ExceptionEnum;
import com.haozi.ehub.core.exception.JWTException;
import com.haozi.ehub.core.model.User;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;

import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.Key;
import java.util.*;

/**
 * @author ：terry
 * @date ：Created in 2020/7/6 17:23
 * @description：TODO
 * @version: 1.0
 */
@SuppressWarnings(value = "all")
public class JWTUtils {

    private static final Logger logger = LoggerFactory.getLogger(JWTUtils.class);

    /**
     * 将SECRET常量字符串使用base64解码成字节数组
     * @return apiKeySecretBytes
     */
    private static byte[] apiKeySecretBytes(){
        return DatatypeConverter.parseBase64Binary(CodeUtils.APPSECRET_KEY);
    }


    private static Key signingKey(){
        //将SECRET常量字符串使用base64解码成字节数组
        //byte[] apiKeySecretBytes = apiKeySecretBytes();
        //使用HmacSHA256签名算法生成一个HS256的签名秘钥Key
        Key signingKey = new SecretKeySpec(
                apiKeySecretBytes(),SignatureAlgorithm.HS256.getJcaName());
        return signingKey;
    }

    /**
     * 创建jwt头部
     * @author     ： terry
     * @date       ： Created in 2020/7/7 12:15
     * @return Map装有jwt头部信息的map集合
     */
    private static Map header(){
        Map<String,Object> header = new HashMap<>();
        //添加头部信息(其实默认也是这个) ╮(╯_╰)╭
        header.put(CodeUtils.JWT_ALG,SignatureAlgorithm.HS256.getValue());
        return header;
    }

    /**
     * 生成Token
     */
    public static String createToken(String username,Map<String,Object> dataMap) {
        String token = Jwts
                .builder()
                .setHeader(header())
                .setSubject(username)
                .setClaims(dataMap)
                .claim(CodeUtils.USER_NAME,username)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + CodeUtils.EXPIRITION))
                .signWith(SignatureAlgorithm.HS256, signingKey()).compact();
        return token;
    }

    /**
     * 生成Token
     */
    public static String createToken(Map<String,Object> dataMap) {
        String token = Jwts
                .builder()
                .setHeader(header())
                .setClaims(dataMap)
                .setIssuedAt(new Date())
                .setExpiration(new Date(System.currentTimeMillis() + CodeUtils.EXPIRITION))
                .signWith(SignatureAlgorithm.HS256, signingKey()).compact();
        return token;
    }

    /**
     * 校验Token
     */
    public static Claims checkJWT(String token) {
        return getClaims(token);
    }

    /**
     * 从Token中获取username
     */
    public static String getUsername(String token){

        Claims claims = getClaims(token);
        return claims.get(CodeUtils.USER_NAME).toString();
    }

    /**
     * 从Token中获取User
     */
    public static User getUser(String token){
        User user = new User();
        Claims claims = getClaims(token);
        //以LinkedHashMap的形式获取token中的用户数据
        LinkedHashMap<String,Object> data = (LinkedHashMap<String, Object>) claims.get(CodeUtils.USER);

        if(data != null && !data.isEmpty()){
            //将LinkedHashMap的形式的用户数据通过json转换成User对象
            String userJson = JSONObject.toJSONString(data);
            user = JSONObject.parseObject(userJson,new TypeReference<User>(){});

            //从LinkedHashMap的形式的用户数据中获取权限数据
            List<LinkedHashMap<String,String>> authorities =
                    (List<LinkedHashMap<String,String>>)data.get(CodeUtils.AUTHORITIES_KEY);

            //创建新的权限集合
            List<GrantedAuthority> list = new ArrayList<>();

            //先将用户对象中的权限设置为空
            user.setAuthorities(null);

            //判断是否能获取权限
            if(authorities != null && authorities.size()>0){
                //从token中获取权限
                for (int i = 0; i < authorities.size(); i++) {
                    for(Map.Entry<String, String> entry : authorities.get(i).entrySet()) {
                        list.add(new SimpleGrantedAuthority(entry.getValue()));
                    }
                }
                //从新设置权限
                user.setAuthorities(list);
            }
        }
        return user;
    }

    /**
     * 从Token中获取用户角色
     */
    public static String getRole(String token){
        Claims claims = getClaims(token);
        return claims.get(CodeUtils.ROLE_CLAIMS).toString();
    }

    /**
     * 从Token中获取用户权限(权限没有加密的情况)
     */
    public static List<GrantedAuthority> getAuthByEcrypt(String token){

        //解析token
        Claims claims = getClaims(token);

        List<LinkedHashMap<String,String>> authorities = new ArrayList<>();
        List<GrantedAuthority> list = new ArrayList<>();
        String authClaims = CodeUtils.AUTH_CLAIMS;
        //从token中直接获取权限，如果权限不存在则从用户中获取权限
        if(claims.get(authClaims) != null){
            //获取加密的权限
            String ecryptAuth = (String) claims.get(authClaims);
            //将加密的权限解密,解密后的结果是JSON字符串
            String authJson = RSAEncoderUtils.decrypt(ecryptAuth);
            //将JSON字符串转换为JSONObject类型的集合
            List<JSONObject> authList = JSONObject.parseObject(authJson, List.class);

            //遍历权限的集合
            authList.forEach(jsonObject -> {
                //从权限的JSON字符串中取出权限名称(或是权限代码)
                String authority = jsonObject.getObject(CodeUtils.AUTHORITY_KEY, String.class);
                //生成新得权限信息
                GrantedAuthority grantedAuthority = new SimpleGrantedAuthority(authority);
                list.add(grantedAuthority);
            });
        }else{
            LinkedHashMap<String,Object> data = (LinkedHashMap<String, Object>) claims.get(CodeUtils.USER);
            authorities = (List<LinkedHashMap<String,String>>)data.get(CodeUtils.AUTHORITIES_KEY);

            //如果权限不为空则将权限重新封装
            if(authorities != null && authorities.size()>0){
                //从token中获取权限
                for (int i = 0; i < authorities.size(); i++) {
                    for(Map.Entry<String, String> entry : authorities.get(i).entrySet()) {
                        System.out.println("key:" + entry.getKey() + "   value:" + entry.getValue());
                        list.add(new SimpleGrantedAuthority(entry.getValue()));
                    }
                }
            }
        }


        return list;
    }

    /**
     * 从Token中获取用户权限(权限没有加密的情况)
     */
    public static List<GrantedAuthority> getAuthByDecrypt(String token){

        //解析token
        Claims claims = getClaims(token);

        List<LinkedHashMap<String,String>> authorities = new ArrayList<>();
        List<GrantedAuthority> list = new ArrayList<>();

        //从token中直接获取权限，如果权限不存在则从用户中获取权限
        if(claims.get(CodeUtils.AUTH_CLAIMS) != null){
            authorities = (List<LinkedHashMap<String,String>>)claims.get(CodeUtils.AUTH_CLAIMS);
        }else{
            LinkedHashMap<String,Object> data = (LinkedHashMap<String, Object>) claims.get(CodeUtils.USER);
            authorities = (List<LinkedHashMap<String,String>>)data.get(CodeUtils.AUTHORITIES_KEY);
        }

        //如果权限不为空则将权限重新封装
        if(authorities != null && authorities.size()>0){
            //从token中获取权限
            for (int i = 0; i < authorities.size(); i++) {
                for(Map.Entry<String, String> entry : authorities.get(i).entrySet()) {
                    System.out.println("key:" + entry.getKey() + "   value:" + entry.getValue());
                    list.add(new SimpleGrantedAuthority(entry.getValue()));
                }
            }
        }

        return list;
    }

    /**
     * 校验Token是否过期
     */
    public static boolean isExpiration(String token){
        return getClaims(token).getExpiration().before(new Date());
    }

    /**
     * 将token解析成Claims
     * @author     ： terry
     * @date       ： Created in 2020/7/10 12:13
     * @param token
     * @return： Claims
     */
    public static Claims getClaims(String token){
        try{
            //解析token
            Claims claims = Jwts.parser().setSigningKey(signingKey()).parseClaimsJws(token).getBody();
            if(claims == null){
                throw new JWTException(ExceptionEnum.TOKEN_ERROR.getEnmsg());
            }
            return claims;
        }catch (Exception e){
            logger.error(e.getMessage());
            //e.printStackTrace();
            if (e instanceof ExpiredJwtException){
                throw new JWTException(ExceptionEnum.TOKEN_EXPIRED.getEnmsg());
            }
        }
        return null;
    }

    public static void main(String[] args) {
        byte[] bytes = apiKeySecretBytes();
        System.out.println(bytes);

        String token = "eyJhbGciOiJIUzI1NiJ9." +
                "eyJhdXRoIjpbeyJhdXRob3JpdHkiOiJST0xFX3N1cF9hZG1pbiJ9XSwiZXhwIjoxNTk0OTY2MjI2LCJpYXQiOjE1OTQzNjE0MjY" +
                "sInVzZXJuYW1lIjoiTklrRW9pYzFVenlPNEZEUm9QRWNqeHVPVVZjVFpDRCtDVk82WjZQakFvOXNRTGhWNWZSQ3QwRFFwSE9mdG" +
                "lGN2xTME5HRVZ0MXhpdVdiODRjWkhTNG80bmdkWkFSalpYV2xjTzQ3c1E1ZEdFeUZLRFJDU1lHUWtUT0VJRlVzOEJDaE9Odm1Wb" +
                "2wzU284VUFxOHI1UGJwYzVodFAwdW96TWxSbW5zTU9wcWxzPSJ9.CaD4Um7pL-VWRWfBTTK7rjOSTnywdGkvqH6jP5FJfsY";

        String encryptToken = "ejA8jJe2LYHnL/" +
                "5QCS2F78nH9gHbEsQ+KUjf3Ex8yKn7EMuTUJKZwMykDmp25bnVmb6T+gfDCnwv0G9vxjBK4kJNlTjY2b51kxGZ9ZASNKx8KO8bz" +
                "oRd+ydrJms04fKh5P4HOjzOfB62xcLG6LFqkcumGgQu5QRlgWmrIq+UbEBv8DLiptj3Xd5A02vxaOc2nIAdzsrVS6yhfyPnmmxL" +
                "1TrwLB4Zqk3iFDg+d4AOZ84SwbenLjLokRFpk8n37YRd6cqIpW21jVifX4MOG0/nsRKoMNWMkQ2AP4reiucErSpJsS8UHbXNDkM" +
                "7xYIBJn72ZOW94Tt2uM3Go4hwpurNyCfVcWMRKM7YtfD/FI9VfDA74+2imYzcZALKugVZCXQj6lbl9aoxBFP4aN0nDTMxjg05ZH" +
                "P217bZHxMZ+hKy7qlFOoaILHoIaxN+7VLBy/m7XNCLKHs5PH2lLgX6kbw5RzsGpCwFHVSNTtCg8McL3Tr9LSIHtCZSeLiqqW/sCB" +
                "5kFP9KQVgwjos94j2AS2YWsYWPbJeke0g1vCCw2m8ldh0BWZ8J9z5l05ZM1ZPQDg/kxdPp/A9/Lc/XiEbBquFD0oBJiGeqVHoMwb" +
                "+pE2r0df+m+xvny012+YlsMYB/My+M/OAPuoiWz9sR4Ky/mPDfb9Ntl6sVWUVR0lNEHEgMlZQ=";

        String hex = HexUtil.encodeHexStr("eyJhbGciOiJIUzI1NiJ9",CharsetUtil.CHARSET_UTF_8);
        System.out.println("hex: "+hex);

        String bytes1 = HexUtil.decodeHexStr(hex);
        System.out.println("bytes1: "+bytes1);

        String encrypt = RSAEncoderUtils.encrypt(token);
        System.out.println("encrypt: "+encrypt);

        String decrypt = RSAEncoderUtils.decrypt(encrypt);
        System.out.println("decrypt: "+decrypt);

        System.out.println(StringUtils.equals(token,decrypt));

        String username = getUsername(token);
        System.out.println("username: "+username);

        System.out.println(isExpiration(token));

        List<GrantedAuthority> auth = getAuthByDecrypt(token);
        auth.forEach(grantedAuthority -> {
            System.out.println(grantedAuthority.getAuthority());
        });

        User user = getUser(token);
        System.out.println(user.getUsername());
    }
}
